﻿using Clancy.Common.Extensions;
using Clancy.Interfaces;
using Clancy.Ui.Properties;
using ReactiveUI;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Reactive;
using System.Reactive.Linq;

namespace Clancy.Ui.ViewModels
{
    public class OnlineConnectionViewModel : ClancyEditorViewModelBase
    {
        private readonly IObservable<bool> m_ConnectionAllowedObservable;
        private readonly IObservable<bool> m_IsConnectedObservable;
        private readonly IObservable<bool> m_IsOnlineModeObservable;
        private readonly IObservable<EventPattern<NotifyCollectionChangedEventArgs>> m_RunningRemoteStreamObservable;
        private readonly ObservableCollection<Guid> m_RunningRemoteStreams = new ObservableCollection<Guid>();
        private readonly IObservable<bool> m_SelectedObjectIsRunningStream;
        private readonly ISelectionManager m_SelectionManager;
        private readonly IServerConnectionLogic m_ServerConnectionLogic;
        private readonly ISurveilanceStorage m_SurveilanceStorage;
        private ObservableAsPropertyHelper<ConnectionStatus> m_ConnectionStatusHelper;
        private bool m_OnlineUpdateAllowed;
        private ObservableAsPropertyHelper<ISelectable> m_SelectedSurveilanceObjectHelper;

        public OnlineConnectionViewModel(IServerConnectionLogic serverConnectionLogic, IOperationalAdapterProvider operationalAdapterProvider, ISelectionManager selectionManager, ISurveilanceStorage surveilanceStorage)
            : base(operationalAdapterProvider, surveilanceStorage)
        {
            m_ServerConnectionLogic = serverConnectionLogic;
            m_SelectionManager = selectionManager;
            m_SurveilanceStorage = surveilanceStorage;

            m_ConnectionAllowedObservable = m_ServerConnectionLogic.ConnectionStatus.Select(conStat => (conStat == ConnectionStatus.NotConnected) || (conStat == ConnectionStatus.ConnectionFailed));
            m_IsOnlineModeObservable = operationalAdapterProvider.OperationMode.Select(mode => mode == OperationMode.Online);
            m_IsConnectedObservable = m_ServerConnectionLogic.ConnectionStatus.Select(conStat => (conStat == ConnectionStatus.Connected));

            InitProperties();

            CreateRemotingCommands();

            FillRibbon();
        }

        public ReactiveCommand<object> CmdConnectToService { get; private set; }

        public ReactiveCommand<object> CmdDisconnectFromService { get; private set; }

        public ReactiveCommand<object> CmdStartRemoteStream { get; private set; }

        public ReactiveCommand<object> CmdStopRemoteStream { get; private set; }

        public ConnectionStatus ConnectionStatus
        {
            get
            {
                if (m_ConnectionStatusHelper == null)
                    return ConnectionStatus.NotConnected;
                return m_ConnectionStatusHelper.Value;
            }
        }

        public bool OnlineUpdateAllowed
        {
            get { return m_OnlineUpdateAllowed; }
            private set
            {
                m_OnlineUpdateAllowed = value;
                this.RaisePropertyChanged();
            }
        }

        private ISurveilanceObject SelectedSurveilanceObject
        {
            get
            {
                if (m_SelectedSurveilanceObjectHelper == null)
                    return null;
                return m_SelectedSurveilanceObjectHelper.Value as ISurveilanceObject;
            }
        }

        protected override void DisposeElements()
        {
        }

        private void CreateRemotingCommands()
        {
            CmdConnectToService = ReactiveCommand.Create(m_IsOnlineModeObservable.CombineLatest(m_ConnectionAllowedObservable, (online, connect) => online && connect));
            CmdConnectToService.Subscribe(async _ =>
            {
                bool connected = await m_ServerConnectionLogic.Connect(OperationAdapter);
                if (connected)
                {
                    ISurveilanceObject[] surveilanceObjects = OperationAdapter.GetAllSurveilances();
                    foreach (ISurveilanceObject surveilanceObject in surveilanceObjects)
                    {
                        m_SurveilanceStorage.SurveilanceObjects.Add(surveilanceObject);
                    }
                }
                OnlineUpdateAllowed = connected;
            });
            DisposableRxElements.Add(CmdConnectToService);

            CmdDisconnectFromService = ReactiveCommand.Create(m_ServerConnectionLogic.ConnectionStatus.Select(connected => connected == ConnectionStatus.Connected));
            CmdDisconnectFromService.Subscribe(_ =>
            {
                m_ServerConnectionLogic.Disconnect();
                OnlineUpdateAllowed = false;
            });
            DisposableRxElements.Add(CmdDisconnectFromService);

            CmdStartRemoteStream = ReactiveCommand.Create(m_IsConnectedObservable.CombineLatest(
                m_SelectionManager.CurrentSelection.Select(sel => (sel as ISurveilanceObject != null)),
                    (con, sel) => con && sel));

            CmdStartRemoteStream.Subscribe(_ => StartRemoteStreamPlayback(SelectedSurveilanceObject));
            DisposableRxElements.Add(CmdStartRemoteStream);

            CmdStopRemoteStream = ReactiveCommand.Create(m_IsConnectedObservable.CombineLatest(
                m_SelectionManager.CurrentSelection.Select(sel => (sel as ISurveilanceObject != null)),
                    (con, sel) => con && sel));

            CmdStopRemoteStream.Subscribe(_ => StopRemoteStreamPlayback(SelectedSurveilanceObject));
            DisposableRxElements.Add(CmdStopRemoteStream);
        }

        private void FillRibbon()
        {
            InsertRibbonButton(Resources.RibbonTab_Online, Resources.RibbonGroup_Online, Resources.RibbonElement_OnlineConnect, Resources.network_connect_2_32.ToBitmapImage(), CmdConnectToService);
            InsertRibbonButton(Resources.RibbonTab_Online, Resources.RibbonGroup_Online, Resources.RibbonElement_OnlineDisconnect, Resources.network_disconnect_2_32.ToBitmapImage(), CmdDisconnectFromService);
            InsertRibbonButton(Resources.RibbonTab_Online, Resources.RibbonGroup_Online, Resources.RibbonElement_OnlineStartStream, Resources.media_playback_start_7_32.ToBitmapImage(), CmdStartRemoteStream);
            InsertRibbonButton(Resources.RibbonTab_Online, Resources.RibbonGroup_Online, Resources.RibbonElement_OnlineStopStream, Resources.media_playback_stop_7_32.ToBitmapImage(), CmdStopRemoteStream);
        }

        private void InitProperties()
        {
            m_ConnectionStatusHelper = new ObservableAsPropertyHelper<ConnectionStatus>(m_ServerConnectionLogic.ConnectionStatus,
                _ => { this.RaisePropertyChanged("ConnectionStatus"); });
            DisposableRxElements.Add(m_ConnectionStatusHelper);

            m_SelectedSurveilanceObjectHelper = new ObservableAsPropertyHelper<ISelectable>(m_SelectionManager.CurrentSelection, _ =>
            {
                this.RaisePropertyChanged("SelectedSurveilanceObject");
            });
        }

        private void StartRemoteStreamPlayback(ISurveilanceObject selectedSurveilanceObject)
        {
            if (selectedSurveilanceObject == null)
                return;
            OperationAdapter.StartVideoStream(selectedSurveilanceObject);
            if (!m_RunningRemoteStreams.Contains(selectedSurveilanceObject.Id))
            {
                m_RunningRemoteStreams.Add(selectedSurveilanceObject.Id);
            }
        }

        private void StopRemoteStreamPlayback(ISurveilanceObject selectedSurveilanceObject)
        {
            if (selectedSurveilanceObject == null)
                return;
            OperationAdapter.StopVideoStream(selectedSurveilanceObject);
            if (m_RunningRemoteStreams.Contains(selectedSurveilanceObject.Id))
            {
                m_RunningRemoteStreams.Remove(selectedSurveilanceObject.Id);
            }
        }
    }
}